﻿

#region Using Statements

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

#endregion


namespace Freeform
{
    /// <summary>
    /// The animation class is used to split a spritesheet up into an animation.
    /// </summary>
    public class Animation2D
    {

        #region Events

        /// <summary>
        /// This event is fired when an animation has finished playing and the animation has entered a freezeframe state.
        /// </summary>
        public event AnimationFinishedEventHandler AnimationFinished;
        public delegate void AnimationFinishedEventHandler(Animation2D Animation);

        #endregion


        #region Members

        /// <summary>
        /// Gets the image data for the sprite or animation.
        /// </summary>
        public virtual Texture2D Texture { get; set; }


        /// <summary>
        /// Gets the width per animation cell.
        /// </summary>
        public virtual Int32 WidthPerCell { get { return Texture.Width / Columns; } }


        /// <summary>
        /// Gets the height per animation cell.
        /// </summary>
        public virtual Int32 HeightPerCell { get { return Texture.Height / Rows; } }


        /// <summary>
        /// Gets or sets the number of rows in the animation.
        /// </summary>
        public virtual Int32 Rows { get; set; }


        /// <summary>
        /// Gets the number of columns in the animation.
        /// </summary>
        public virtual Int32 Columns { get; set; }


        /// <summary>
        /// Gets the number of frames in the animation.
        /// </summary>
        public virtual Int32 Frames { get { return Rows * Columns; } }


        /// <summary>
        /// Gets or sets the number of frames per second to play for an animation.
        /// </summary>
        public virtual Single FrameRate { get; set; }


        /// <summary>
        /// The number of times that the animation should be played. 1 --> Play Once, 2 --> Twice etc..
        /// 0 --> Loop forever.
        /// </summary>
        public virtual Int32 Loop { get; set; }


        /// <summary>
        /// Gets the number of times the animation has played.
        /// </summary>
        private Int32 PlayCount { get; set; }


        /// <summary>
        /// Gets or sets whether the animation should be frozen.
        /// </summary>
        public virtual Boolean FreezeFrame { get; set; }


        /// <summary>
        /// Gets the name of this animation.
        /// </summary>
        public virtual String AnimationName { get; private set; }


        /// <summary>
        /// The milliseconds until the nextframe in the animation should be played.
        /// </summary>
        protected virtual Int32 NextFrame { get; set; }


        /// <summary>
        /// The index of the current frame being played.
        /// </summary>
        protected virtual Int32 CurrentFrame { get; set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new Animation2D.
        /// </summary>
        /// <param name="TextureName">The name of the asset to load.</param>
        /// <param name="AnimationName">The name of this animation. (This string is passed to the AnimationSet to tell the animation to play.)</param>
        /// <param name="Rows">The number of rows in the sprite-sheet.</param>
        /// <param name="Cols">The number of columns in the sprite-sheet.</param>
        /// <param name="Framerate">The framerate of the animation.</param>
        public Animation2D(String TextureName, String AnimationName, int Rows, int Cols, float Framerate)
        {
            Init(TextureName, AnimationName, Rows, Cols, Framerate);
        }


        /// <summary>
        /// Creates a new Animation2D with a single sprite frame.
        /// </summary>
        /// <param name="TextureName">The name of the asset to load.</param>
        /// <param name="AnimationName">The name of the animation.</param>
        public Animation2D(String TextureName, String AnimationName)
        {
            Init(TextureName, AnimationName, 1, 1, 1);
        }


        /// <summary>
        /// Creates a new Animation2D with single sprite frame and name: "Default"
        /// </summary>
        /// <param name="TextureName">The name of the asset to load.</param>
        public Animation2D(String TextureName)
        {
            Init(TextureName, "Default", 1, 1, 1);
        }


        /// <summary>
        /// This initialisation function is shared by the Animation2D constructors.
        /// </summary>
        /// <param name="TextureName">The name of the asset to load.</param>
        /// <param name="AnimationName">The name of the animation.</param>
        /// <param name="Rows"># rows in the animation.</param>
        /// <param name="Cols"># cols in the animation.</param>
        /// <param name="Framerate">Framerate of the animation.</param>
        private void Init(String TextureName, String AnimationName, int Rows, int Cols, float Framerate)
        {
            Texture = Game.GameRef.Content.Load<Texture2D>(TextureName);
            this.Rows = Rows;
            Columns = Cols;
            FrameRate = Framerate;
            this.AnimationName = AnimationName;
            NextFrame = (int)(1000.0f / FrameRate);
        }

        #endregion


        #region Utility

        /// <summary>
        /// Plays the animation.
        /// </summary>
        public virtual void Play()
        {
            PlayCount = 0;
            CurrentFrame = 0;
            FreezeFrame = false;
            NextFrame = (int)(1000.0f / FrameRate);
        }

        #endregion


        #region Update

        /// <summary>
        /// Returns the required source rectangle to access the current frame in the spritesheet.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        /// <returns>The source rectangle to be used in conjunction with the Texture to draw the correct frame.</returns>
        public virtual Rectangle UpdateSource(GameTime Time)
        {
            // Update time till next frame
            if (!FreezeFrame)
            {
                NextFrame -= Time.ElapsedGameTime.Milliseconds;
                if (NextFrame < 0)
                {
                    CurrentFrame++;
                    NextFrame += (int)(1000.0f / FrameRate);

                    if (CurrentFrame >= Frames)
                    {
                        PlayCount++;
                        if (PlayCount < Loop || Loop == 0)
                            CurrentFrame = 0;
                        else
                        {
                            CurrentFrame = Frames - 1;
                            FreezeFrame = true;
                            if (AnimationFinished != null)
                                AnimationFinished(this);
                        }
                    }
                }
            }

            // Generate source rectangle
            int width = Texture.Width / Columns;
            int height = Texture.Height / Rows;
            return new Rectangle(width * (CurrentFrame % Columns), height * (CurrentFrame / Columns), width, height);
        }

        #endregion

    }
}
